package Controller;

import java.awt.Color;
import java.awt.Font;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;

import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JLayeredPane;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.Popup;
import javax.swing.PopupFactory;
import javax.swing.border.Border;
import javax.swing.border.TitledBorder;

import Ui.TradeFrame;

import Messaging.Notification;
import Messaging.NotificationManager;
import Model.ChanceCardSpace;
import Model.CommCardSpace;
import Model.CornerSpace;
import Model.Dice;
import Model.Player;
import Model.PlayerInJail;
import Model.PropertySpace;
import Model.Space;
import Model.TaxSpace;
import Model.UpgradeablePropertySpace;
import Model.UtilitySpace;
import Ui.ActionMessage;
import Ui.DicePanel;
import Ui.Display;
import Ui.DisplayAssembler;
import Ui.GameButtonPanel;
import Ui.JailPopUp;
import Ui.MessagePopUp;
import Ui.Piece;
import Ui.PlayerPanel;
import Ui.PropertySelectionPanel;

/*
 *  @author Paolo Cordova
 */

public class GameController implements ActionListener 
{
	Dice Dice_;
	ArrayList<Player> Players_;
	ArrayList<Piece> Pieces_;
	Space Board_[];
	String[] NamesAndIcons_;
	Display Display_;
	DicePanel DicePanel_;
	GameButtonPanel ButtonPanel_;
	PlayerPanel PlayerPanel_;
	PropertySelectionPanel PropertySelectionPanel_;
	TradeFrame tradeFrame_;
	
	ChanceCardSpace Chance_;
	CommCardSpace CommChest_;
	
	final int NUM_OF_SPACES = 40;
	int currentPlayerNum_;
	int NumOfPlayers_ = 1;
	
	public GameController(Display display) 
	{
		Dice_ = new Dice();
		Board_ = new Space[NUM_OF_SPACES];
		Display_ = display;
		
		NotificationManager.getInstance().addObserver(Notification.START_GAME, this, "StartGame");
		NotificationManager.getInstance().addObserver(Notification.DICE_ANIMATION_DONE, this, "MoveCurrentPlayer");
		NotificationManager.getInstance().addObserver(Notification.GO_TO_JAIL, this, "SendPlayerToJail");
		NotificationManager.getInstance().addObserver(Notification.CARD_MOVE, this, "CardMoveTo");
		NotificationManager.getInstance().addObserver(Notification.UNOWNED_PROPERTY, this, "UnownedProperty");
		NotificationManager.getInstance().addObserver(Notification.PIECE_MOVE_SPACES, this, "PieceMoveSpaces");
		NotificationManager.getInstance().addObserver(Notification.PIECE_MOVE_TO, this, "PieceMoveTo");
		NotificationManager.getInstance().addObserver(Notification.ACTION_MESSAGE, this, "DisplayActionMessage");
		NotificationManager.getInstance().addObserver(Notification.UTILITY_RENT, this, "ChargeUtilityRent");
		NotificationManager.getInstance().addObserver(Notification.REMOVE_PLAYER, this, "RemovePlayer");
		NotificationManager.getInstance().addObserver(Notification.END_TURN, this, "GameOver");
	}
	
	public void StartGame(Object obj) 
	{
		NamesAndIcons_ =(String[]) obj;
		NumOfPlayers_ = Integer.parseInt(NamesAndIcons_[0]);
		
		CreatePlayers();
		CreateSpaces();		
		ShuffleCards();
		
		PlayerPanel_ = new PlayerPanel(Players_);
		DicePanel_ = new DicePanel(Dice_);
		ButtonPanel_ = new GameButtonPanel(this);
	}
	
	public void MoveCurrentPlayer(Object obj) 
	{	
		try 
		{
			Dice dice = (Dice)obj;
			Player currentPlayer = Players_.get(currentPlayerNum_);
			currentPlayer.movePiece(dice);
			Board_[currentPlayer.getPosition()].landOn(currentPlayer);	
		} 
		
		catch (Exception e) 
		{
			e.printStackTrace();
		}
	}
	
	public void MoveCurrentPlayerInteger(Object obj) 
	{
		try 
		{
			Integer numSpaces = (Integer)obj;
			Player currentPlayer = Players_.get(currentPlayerNum_);
			currentPlayer.movePiece(numSpaces);
			Board_[currentPlayer.getPosition()].landOn(currentPlayer);	
		}
		
		catch (Exception e) 
		{
			e.printStackTrace();
		}
	}

	public void PieceMoveTo(Object obj) 
	{
		Integer spaceNum = (Integer) obj;
		Pieces_.get(currentPlayerNum_).moveToSpace(spaceNum);
	}
	
	public void PieceMoveSpaces(Object obj) 
	{
		Integer spaceNum = (Integer) obj;
		Pieces_.get(currentPlayerNum_).move(spaceNum);
	}

	public void SendPlayerToJail() 
	{
		Players_.get(currentPlayerNum_).goToJail();
	}

	public void RemovePlayer() 
	{
		Pieces_.get(currentPlayerNum_).removePiece();
		Pieces_.remove(currentPlayerNum_);
		Players_.remove(currentPlayerNum_);
		PlayerPanel_.removePanel(currentPlayerNum_);
		NumOfPlayers_--;
		
		if(Players_.size() == 1) 
		{
			ActionMessage.getInstance().newMessage(Players_.get(0).getName() + " Won! ");
			ButtonPanel_.setAllDisabled();
			DicePanel_.setDisabled();
		} 
		else 
		{
			currentPlayerNum_ = (currentPlayerNum_ + 2) % NumOfPlayers_;
			disposeFrames();
			NotificationManager.getInstance().notifyObservers(Notification.END_TURN, new Integer(currentPlayerNum_));
		}
	}
	
	public void GameOver() 
	{
		if(Players_.size() == 0) 
		{
			ActionMessage.getInstance().newMessage(Players_.get(0).getName() + " Won!");
			ButtonPanel_.setAllDisabled();
			DicePanel_.setDisabled();
		}
	}
		
	private void CreatePlayers() 
	{
		Players_ = new ArrayList<Player>();
		Pieces_ = new ArrayList<Piece>();
		
		for (int i = 0; i < NumOfPlayers_; i++) {
			Players_.add(new Player(i, NamesAndIcons_[i + 1]));
			Pieces_.add(new Piece(NamesAndIcons_[NumOfPlayers_ + i + 1], i + 1));
		}
	}

	public void ChargeUtilityRent(Object obj) 
	{
		UtilitySpace property = (UtilitySpace) obj;
		int fee = property.getMultiplier() * Dice_.getTotalRoll();
		
		property.getOwner().collectRent(fee, Players_.get(currentPlayerNum_));
	}
	
	private void CreateSpaces() 
	{
		Chance_ = ChanceCardSpace.Instance(Players_);
		CommChest_ = CommCardSpace.Instance(Players_);

		Board_[0] = new CornerSpace("Go");
		Board_[1] = new UpgradeablePropertySpace("Mediterranean Avenue", 0, 1, 60, 30, 2, 10, 30, 90, 160, 250);
		Board_[2] = CommChest_;
		Board_[3] = new UpgradeablePropertySpace("Baltic Avenue", 0, 3, 60, 30, 4, 20,	60,	180, 320, 450);
		Board_[4] = new TaxSpace("Income Tax");
		Board_[5] = new PropertySpace("Reading Railroad", 8, 5, 200, 100, 25, 50, 100, 200);
		Board_[6] = new UpgradeablePropertySpace("Oriental Avenue", 1, 6, 100, 30, 6,	30,	90,	270, 400, 550);
		Board_[7] = Chance_;
		Board_[8] = new UpgradeablePropertySpace("Vermont Avenue", 1, 8, 100, 30, 6, 30, 90, 270, 400, 550);
		Board_[9] = new UpgradeablePropertySpace("Connecticut Avenue", 1, 9, 120, 30, 8, 40, 100, 300, 450, 600);
		Board_[10] = new CornerSpace("In Jail");
		Board_[11] = new UpgradeablePropertySpace("St. Charles Place", 2, 11, 140, 70, 10, 50, 150, 450, 625, 750);
		Board_[12] = new UtilitySpace("Electric Company", 9, 12, 150, 75);
		Board_[13] = new UpgradeablePropertySpace("States Avenue", 2, 13, 140, 70, 10, 50, 150, 450, 625, 750);
		Board_[14] = new UpgradeablePropertySpace("Virginia Avenue", 2, 14, 160, 80, 12, 60, 180, 500, 700, 900);
		Board_[15] = new PropertySpace("Pennsylvania Railroad", 8, 15, 200, 100, 25, 50, 100, 200);
		Board_[16] = new UpgradeablePropertySpace("St. James Place", 3, 16, 180, 90, 14, 70, 200, 550, 750, 950);
		Board_[17] = CommChest_;
		Board_[18] = new UpgradeablePropertySpace("Tennessee Avenue", 3, 18, 180, 90, 14, 70, 200, 550, 750, 950);
		Board_[19] = new UpgradeablePropertySpace("New York Avenue", 3, 19, 200,100, 16, 80, 220, 600, 800, 1000);
		Board_[20] = new CornerSpace("Free Parking");
		Board_[21] = new UpgradeablePropertySpace("Kentucky Avenue", 4, 21, 220, 110, 18, 90, 250, 700, 875, 1050);
		Board_[22] = Chance_;
		Board_[23] = new UpgradeablePropertySpace("Indiana Avenue", 4, 23, 220, 110, 18, 90, 250, 700, 875, 1050);
		Board_[24] = new UpgradeablePropertySpace("Illinois Avenue", 4, 24, 240, 120, 20, 100, 300, 750, 925, 1100);
		Board_[25] = new PropertySpace("B.&O. Railroad", 8, 25, 200, 100, 25, 50, 100, 200);
		Board_[26] = new UpgradeablePropertySpace("Atlantic Avenue", 5, 26, 260, 130, 22, 110, 330, 800, 975, 1150);
		Board_[27] = new UpgradeablePropertySpace("Ventnor Avenue", 5, 27, 260, 130, 22, 110, 330, 800, 975, 1150);
		Board_[28] = new UtilitySpace("Water Works", 9, 28, 150, 75);
		Board_[29] = new UpgradeablePropertySpace("Marvin Gardens", 5, 29, 280, 140, 24, 120, 360, 850, 1025, 1200);
		Board_[30] = new CornerSpace("Go To Jail!");
		Board_[31] = new UpgradeablePropertySpace("Pacific Avenue", 6, 31, 300, 150, 26, 130, 390, 900, 1100, 1275);
		Board_[32] = new UpgradeablePropertySpace("North Carolina Avenue", 6, 32, 300, 150, 26, 130, 390, 900, 1100, 1275);
		Board_[33] = CommChest_;
		Board_[34] = new UpgradeablePropertySpace("Pennsylvania Avenue", 6, 34, 320, 160, 28, 150, 450, 1000, 1200, 1400);
		Board_[35] = new PropertySpace("Short Line", 8, 35, 200, 100, 25, 50, 100, 200);
		Board_[36] = Chance_;
		Board_[37] = new UpgradeablePropertySpace("Park Place", 7, 37, 350, 175, 35, 175, 500, 1100, 1300, 1500);
		Board_[38] = new TaxSpace("Luxury Tax");
		Board_[39] = new UpgradeablePropertySpace("Boardwalk", 7, 39, 400, 200, 50, 200, 600, 1400, 1700, 2000);
		
		Display_.showBoard(Board_);	
	}
	
	public void ShuffleCards() 
	{
		Chance_.ShuffleCards();
		CommChest_.ShuffleCards();
	}

	public void actionPerformed(ActionEvent action) 
	{
		if (action.getActionCommand().equals("Purchase")) 
		{
			disposeFrames();
			int position = Players_.get(currentPlayerNum_).getPosition();
			Players_.get(currentPlayerNum_).purchase((PropertySpace)Board_[position]);
		}
		
		else if (action.getActionCommand().equals("Properties")) 
		{
			disposeFrames();
			
			PropertySelectionPanel_ = new PropertySelectionPanel(Players_.get(currentPlayerNum_));
		}
		
		else if (action.getActionCommand().equals("Trade")) 
		{
			disposeFrames();
			
			tradeFrame_ = new TradeFrame(Players_, currentPlayerNum_, false);
		}
		
		else if (action.getActionCommand().equals("End Turn")) 
		{
			currentPlayerNum_ = (currentPlayerNum_ + 1) % NumOfPlayers_;
			disposeFrames();
			
			NotificationManager.getInstance().notifyObservers(Notification.END_TURN, new Integer(currentPlayerNum_));
			if ((Players_.get(currentPlayerNum_).getState() == PlayerInJail.Instance())
					&& (Players_.get(currentPlayerNum_).getNumOfRolls() < 2))
				new JailPopUp(Players_.get(currentPlayerNum_));
		}
		
		else if (action.getActionCommand().equals("Quit Game")) 
		{
			ConfirmationPopUp();
		}
		
	}

	public void disposeFrames() 
	{
		if (PropertySelectionPanel_ != null) {
			PropertySelectionPanel_.dispose();
			PropertySelectionPanel_ = null;
		}
		
		if (tradeFrame_ != null) {
			tradeFrame_.dispose();
			tradeFrame_ = null;
		}
	}

	public void CardMoveTo(Object obj) 
	{
		System.out.println("cardmoveTo");
		Integer num = (Integer)obj;
		Board_[(int)num].landOn(Players_.get(currentPlayerNum_));
		
	}

	public void ConfirmationPopUp() 
	{
		JPanel basePanel = new JPanel();
		JPanel panel1 = new JPanel();
		JPanel panel2 = new JPanel();

		int hGap = 10, vGap = 10;
		GridLayout baseGridLayout = new GridLayout(2, 1, hGap, vGap);
		GridLayout gridLayout1 = new GridLayout(1, 1, 0, 0);
		GridLayout gridLayout2 = new GridLayout(1, 2, 0, 0);
		
		basePanel.setLayout(null);
		panel1.setLayout(null);
		panel2.setLayout(null);
		Font labelFont = new Font("SansSerif", Font.BOLD, 20);
		Font buttonFont = new Font("SansSerif", Font.BOLD, 18);
		Font titleFont = new Font("SansSerif", Font.BOLD, 14);
		
		JLabel label = new JLabel("  Are you sure you want to quit?  ");
		label.setFont(labelFont);
		label.setForeground(Color.white);
		panel1.setLayout(gridLayout1);
		panel1.add(label);
		panel1.setBackground(new Color(0, 0, 0));
		
		JButton quit = new JButton("Quit");
		quit.setFont(buttonFont);
		quit.setVisible(true);
		
		JButton cancel = new JButton("Cancel");
		cancel.setFont(buttonFont);
		cancel.setVisible(true);
		
		panel2.add(quit);
		panel2.add(cancel);
		panel2.setLayout(gridLayout2);
		
		basePanel.setLayout(baseGridLayout);
		basePanel.setBackground(new Color(0, 0, 0));
		
		Border blackline = BorderFactory.createLineBorder(Color.white, 2);
		TitledBorder title = BorderFactory.createTitledBorder(blackline, "  Quit Game ");
		title.setTitleColor(Color.white);
		title.setTitleFont(titleFont);
		title.setTitleJustification(TitledBorder.LEFT);
		basePanel.setBorder(title);
		basePanel.add(panel1);
		basePanel.add(panel2);
		basePanel.setVisible(true);
	
		int xCoord = (int)(DisplayAssembler.getScreenWidth() - basePanel.getPreferredSize().getWidth()) / 2;
		int yCoord = (int)(DisplayAssembler.getScreenHeight() - basePanel.getPreferredSize().getHeight()) / 2;
		
		PopupFactory factory = PopupFactory.getSharedInstance();
		final Popup popup = factory.getPopup(null, basePanel, xCoord, yCoord);
		
		quit.addActionListener(new ActionListener() 
		{
	        public void actionPerformed(ActionEvent event) 
	        {
	        	popup.hide();
	        	NotificationManager.getInstance().notifyObservers(Notification.REMOVE_CARD, null);
	        	System.exit(0);
	        }
		});
		
		cancel.addActionListener(new ActionListener() 
		{
	        public void actionPerformed(ActionEvent event) 
	        {
	        	popup.hide();
	        	NotificationManager.getInstance().notifyObservers(Notification.REMOVE_CARD, null);
	        }
	    });
	
		popup.show();	
		NotificationManager.getInstance().notifyObservers(Notification.SHOW_CARD, null);
	}
	
	public void DisplayActionMessage(Object obj) 
	{
		try 
		{
			String message = (String) obj;
			ActionMessage.getInstance().newMessage(message);
			
		}
		
		catch (ClassCastException e) 
		{
			System.err.println("Unknown object passed to method DisplayActionMessage");
		}
	}

	public void UnownedProperty(Object obj) 
	{
		try 
		{
			PropertySpace property = (PropertySpace) obj;
			new MessagePopUp(property.getName() + " is unowned. To purchase it for $" + property.getPurchasePrice() + ", use the purchase button.");
		} 		
		
		catch (ClassCastException e) 
		{
			System.err.println("Unknown object passed to method UnownedProperty");
		}
	}
}
